import { 
  AmbientLight, 
  AxesHelper, 
  BoxGeometry, 
  Clock, 
  Face3, 
  Fog, FogExp2, 
  Geometry, 
  Mesh, MeshBasicMaterial, MeshLambertMaterial, 
  PerspectiveCamera, PlaneGeometry, Scene, SpotLight, 
  Vector3, 
  WebGLRenderer 
} from "three"
import { SceneUtils } from 'three/examples/jsm/utils/SceneUtils'
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls'
import Stats from 'stats.js'
import dat from 'dat.gui'

const scene = new Scene()
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000)
const renderer = new WebGLRenderer()
renderer.setClearColor(0x000000)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true

document.body.appendChild(renderer.domElement)

// 创建一个平面
const planeGeometry = new PlaneGeometry(60, 40, 1, 1)
const planeMaterial = new MeshLambertMaterial({ color: 0xffffff })
const plane = new Mesh(planeGeometry, planeMaterial)
// 接收阴影
plane.receiveShadow = true

plane.rotation.x = -0.5 * Math.PI
plane.position.x = 0
plane.position.y = 0
plane.position.z = 0

scene.add(plane)

// 添加坐标系，方便理解
const axes = new AxesHelper(15)
scene.add(axes)

// 设置相机位置
camera.position.x = -30
camera.position.y = 40
camera.position.z = 30
camera.lookAt(new Vector3(5, 0, 0))

// 添加环境光
const ambientLight = new AmbientLight(0x494949)
scene.add(ambientLight)

// 添加顶点
let vertices = [
  new Vector3(1, 3, 1),
  new Vector3(1, 3, -1),
  new Vector3(1, -1, 1),
  new Vector3(1, -1, -1),
  new Vector3(-1, 3, -1),
  new Vector3(-1, 3, 1),
  new Vector3(-1, -1, -1),
  new Vector3(-1, -1, 1),
]

// 添加面
let faces = [
  new Face3(0, 2, 1),
  new Face3(2, 3, 1),
  new Face3(4, 6, 5),
  new Face3(6, 7, 5),
  new Face3(4, 5, 1),
  new Face3(5, 0, 1),
  new Face3(7, 6, 2),
  new Face3(6, 3, 2),
  new Face3(5, 7, 0),
  new Face3(7, 2, 0),
  new Face3(1, 3, 4),
  new Face3(3, 6, 4),
]

let geom = new Geometry()
geom.vertices = vertices
geom.faces = faces
// 重新计算侧面，更新图形
geom.computeFaceNormals()

let materials = [
  // 生成线框材质
  new MeshBasicMaterial({color: 0x000000, wireframe: true}),
  // 生成面材质
  new MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true})
]
// 创建网格
let mesh = SceneUtils.createMultiMaterialObject(geom, materials)
// 投射阴影
mesh.castShadow = true
mesh.children.forEach(e => {
  e.castShadow = true
})

scene.add(mesh)

// 添加聚光灯
const spotLight = new SpotLight(0xffffff, 1, 180, Math.PI/4)
spotLight.shadow.mapSize.height = 2048
spotLight.shadow.mapSize.width = 2048
spotLight.position.set(-40, 30, 30)
spotLight.castShadow = true
spotLight.lookAt(scene.position)
scene.add(spotLight)

// 添加 FPS 监控
let stats: Stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)

function addControl(x: number, y: number, z: number) {
  let controls = new function() {
    this.x = x
    this.y = y
    this.z = z
  }

  return controls
}

let controlPoints = []
controlPoints.push(addControl(3, 5, 3))
controlPoints.push(addControl(3, 5, 0))
controlPoints.push(addControl(3, 0, 3))
controlPoints.push(addControl(3, 0, 0))
controlPoints.push(addControl(0, 5, 0))
controlPoints.push(addControl(0, 5, 3))
controlPoints.push(addControl(0, 0, 0))
controlPoints.push(addControl(0, 0, 3))

// 控制器
const gui = new dat.GUI()
gui.add(new function () {
  this.clone = function () {
    let cloneGeometry: Geometry = (<any>mesh.children[0]).geometry.clone()
    let materials = [
      new MeshLambertMaterial({opacity: .8, color: 0xff44ff, transparent: true}),
      new MeshBasicMaterial({color: 0x000000, wireframe: true})
    ]
    // 创建新的网格
    let mesh2 = SceneUtils.createMultiMaterialObject(cloneGeometry, materials)
    mesh2.children.forEach(e => {
      e.castShadow = true
    })

    mesh2.translateX(5)
    mesh2.translateZ(5)
    mesh2.name = 'clone'
    // 删除之前生成的副本网格
    scene.remove(scene.getObjectByName('clone'))
    // 添加新的副本网格
    scene.add(mesh2)
  }
}, 'clone')

let f1

for (let i = 0; i < 8; i++) {
  f1 = gui.addFolder('Vertices'+(i + 1))
  f1.add(controlPoints[i], 'x', -10, 10)
  f1.add(controlPoints[i], 'y', -10, 10)
  f1.add(controlPoints[i], 'z', -10, 10)
}

// 引入轨迹球控制器，添加鼠标控制功能
const trackballControls = new TrackballControls(camera, renderer.domElement)
const clock = new Clock()

function render() {
  trackballControls.update()
  stats.update()

  let vertices = []
  for (let i = 0; i < 8; i++) {
    vertices.push(new Vector3(
      controlPoints[i].x, 
      controlPoints[i].y, 
      controlPoints[i].z
    ))
  }

  mesh.children.forEach((e: any) => {
    e.geometry.vertices = vertices
    e.geometry.verticesNeedUpdate = true
    e.geometry.computeFaceNormals()
    delete e.geometry.__directGeometry
  })

  requestAnimationFrame(render)
  renderer.render(scene, camera)
}
render()